Go 依赖注入工具 wire
Wire 是一个的 Go 依赖注入工具,通过自动生成代码的方式在编译期完成依赖注入
go get github.com/google/wire/cmd/wire
都知道要依赖反转,如下这种方式
func initApp() *App {
c := GetRedisConf()
r := NewRedis(c)
app := NewApp(r)
return app
}
func main() {
app := initApp()
app.Run()
}
但是上面那种手工依赖注入也存在一些问题
目前传入的参数都基本只有一个,这样手写注入过程还可以,一旦你要维护的东西多了,比如你的 NewApp 是这样的
func NewApp(r *Redis, es *ES, us *UserSerivce, db *MySQL) *App
然后其中 UserService 是这样的
func UserService(pg *Postgres, mm *Memcached)
这样形成了多层次的一堆依赖需要注入,徒手去写非常麻烦。
而这部分,就是 wire 这样的依赖注入工具能够起作用的地方了——他的功能只是通过生成代码帮你注入依赖,而 实际的依赖实例需要你自己创建(初始化)。
wire 中的两个概念:
- Provider:负责创建对象的方法(就是类似于 Spring 中的 Bean)
- Injector:负责根据对象的依赖,依次构造依赖对象,最终构造目的对象的方法
示例代码:
首先要实现一个 wire.go
的文件,里面分别实现好 Provider。
func GetRedisConf() *RedisConfig {
// ...
}
func NewRedis(conf *RedisConfig) DataSource {
// ...
}
func SomeProviderSet() *SomeProvider {
// ...
}
func NewApp(ds DataSource) *App {
return &App{ds: ds}
}
然后定义好 Injector。
// +build wireinject
func initApp() (*App) {
panic(wire.Build(GetRedisConf, NewRedis, SomeProviderSet, NewApp))
}
执行 wire 命令后 他会扫描整个项目,并帮你生成一个 wire_gen.go
文件,如果你有什么没有实现好,它会报错出来。
提示
执行 wire 命令生成代码,工具会扫描你的代码,依照你的 Injector 定义来组织各个 Provider 的执行顺序,并自动按照 Provider 们的类型需求来按照顺序执行和安排参数传递,如果有哪些 Provider 的要求没有满足,会在终端报出来,持续修复执行 wire,直到成功生成 wire_gen.go
文件。
它生成的代码其实就是类似我们之前需要手写的这个
func initApp() *App { // injector
c := GetRedisConf() // provider
r := NewRedis(c) // provider
app := NewApp(r) // provider
return app
}
打包注入
wire 里面还有个 ProviderSet 的概念,就是把一组 Provider 打包
举例:
// +build wireinject
var dbLogSet = wire.NewSet(Logger, dbProvider)
// 然后实现各个方法
func Logger(ctx context.Context) log {
// ...
}
func dbProvider(ctx context.Context) *gorm.DB {
// ...
}
func NewTestService(ctx context.Context) service.TestService {
// 这里引用了上面定义的 dbLogSet
wire.Build(service.NewTestService, dbLogSet, redisCacheProvider)
return nil
}
生成的代码:
// 省略...
func NewTestService(ctx context.Context) service.TestService {
db := dbProvider(ctx)
wrapYKLogger := Logger(ctx)
redisUtil := redisCacheProvider(ctx)
testService := service.NewTestService(ctx, wrapYKLogger, redisUtil)
return testService
}